package cn.minimelon.solon.utils;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.*;

/**
 * Excel 操作工具类 其中涉及几个文件流操作和转换 对于有列合并的excel不好用
 */
@Slf4j
public class Excel2007ReadUtils {

    private static boolean isRowNullOrTwentyCellNull4(XSSFRow row) {
        if (row == null) { // 如果此行没有数据，则直接跳出循环，进入下一sheet页
            return true;
        }

        for (int i = 0; i < 20 || i < getLastCellNum(row); i++) {
            if (!"".equals(StrUtil.toString(getStringCellValue(row.getCell(i))))) {
                return false;
            }
        }
        return true;
    }

    private Excel2007ReadUtils() {}

    /**
     * 获取所有的sheet内容
     *
     * @param input 文件流
     * @param rowAt 每个sheet 从rowAt行开始读取数据直至数据结束
     * @return Map<sheetName, List < j, String>>
     */
    public static Map<String, List<Map<Integer, String>>> readExcels4AllSheet(InputStream input, int rowAt) {
        Map<String, List<Map<Integer, String>>> content = new LinkedHashMap<String, List<Map<Integer, String>>>();
        try {
            XSSFWorkbook wb = new XSSFWorkbook(input);
            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                String sheetName = wb.getSheetName(i);
                List<Map<Integer, String>> sheetValue = readExcels(wb, i, rowAt);
                content.put(sheetName, sheetValue);
            }

            return content;
        } catch (IOException e) {
            log.error("error>{}", e.getMessage());
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                log.error("error>{}", e.getMessage());
            }
        }
        return null;
    }

    /**
     * 获取第一个的sheet内容
     *
     * @param input 文件流
     * @param sheetAt 表格Sheet索引，从0开始
     * @param rowAt 从rowAt行开始读取数据直至数据结束
     * @return Map<sheetName, List < j, String>>
     */
    public static List<Map<Integer, String>> readExcels(InputStream input, int sheetAt, int rowAt) {
        try {
            XSSFWorkbook xwb = new XSSFWorkbook(input);
            return readExcels(xwb, sheetAt, rowAt);
        } catch (IOException e) {
            log.error("error>{}", e.getMessage());
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                log.error("error>{}", e.getMessage());
            }
        }
        return null;

    }

    /**
     * 读取Office 2007 excel
     */
    private static List<Map<Integer, String>> readExcels(XSSFWorkbook xwb, int sheetAt, int rowAt) throws IOException {
        List<Map<Integer, String>> contentJson = new LinkedList<>();
        // 读取第一章表格内容
        XSSFSheet sheet = xwb.getSheetAt(sheetAt);
        Object value = null;
        XSSFRow row = null;
        XSSFCell cell = null;
        for (int i = rowAt; i <= sheet.getPhysicalNumberOfRows(); i++) {
            row = sheet.getRow(i);
            if (row == null || isRowNullOrTwentyCellNull4(row)) {
                continue;
            }
            Map<Integer, String> params = new LinkedHashMap<>();
            int lastNum = getLastCellNum(row);
            for (int j = row.getFirstCellNum(); j < lastNum; j++) {
                cell = row.getCell(j);
                if (null != cell) {
                    value = getStringCellValue(cell);
                }
                params.put(j, StrUtil.toString(value));
            }

            contentJson.add(params);
        }

        return contentJson;
    }

    /**
     * 获取单元格数据内容为字符串类型的数据
     */
    private static String getStringCellValue(XSSFCell cell) {
        return getCellValue(cell);
    }

    /**
     * 获取行 内容不为空的最后一列
     */
    private static short getLastCellNum(Row row) {
        // 最后一个有效单元格的位置
        short lastNum = 0;
        if (row.getLastCellNum() < 255) {
            return row.getLastCellNum();
        }
        for (int i = 0; i < 255; i++) {
            // 如果当前单元格不是null 并且有值
            if (!"".equals(getCellValue(row.getCell(i)))) {
                lastNum = (short) i;
            }
        }
        return lastNum;
    }

    /**
     * 数据导入时进行类型转换
     *
     * @param cell excel单元格对象
     * @return string 经过处理后的字符串对象
     */
    private static String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        if (CellType.STRING == cell.getCellType()) { // 字符串格式（默认）
            return StrUtil.toString(cell.getRichStringCellValue());
        } else if (CellType.BOOLEAN == cell.getCellType()) {// 布尔
            return StrUtil.toString(cell.getBooleanCellValue());
        } else if (CellType.NUMERIC == cell.getCellType()) {// 数字格式
            // 解决POI导入Excel日期识别成数字的方法
            if (DateUtil.isCellDateFormatted(cell)) {
                Date date = cell.getDateCellValue();
                return cn.hutool.core.date.DateUtil.formatDateTime(date);
            } else {
                String str = StrUtil.toString(cell.getNumericCellValue());
                // 转换double科学计数法，为字符串 || 单元格整数 去掉小数点'.0'
                if (str.indexOf("E") > 0 || str.indexOf(".0") > 0) {
                    DecimalFormat decimalFormat = new DecimalFormat("#.############"); // #
                    // 一个数字，不包括 0
                    str = decimalFormat.format(Double.parseDouble(str));
                }
                return str;
            }
        } else if (CellType.FORMULA == cell.getCellType()) { // 公式
            return StrUtil.toString(cell.getCellFormula());
        } else if (CellType.ERROR == cell.getCellType()) {
            String msg = StrUtil.format("第{}行第{}列单元格有错误，请核实原始模板！", Integer.toString(cell.getRowIndex() + 1),
                    Integer.toString(cell.getColumnIndex() + 1));
            throw new RuntimeException(msg);
        } else {
            return StrUtil.toString(cell.getStringCellValue());
        }
    }
}
